package com.haifeng.wdsh.gateway.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.haifeng.wdsh.common.base.constants.Constants;
import com.haifeng.wdsh.gateway.exception.ExceptionHandler;
import com.haifeng.wdsh.gateway.exception.GatewayLimitBlockException;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;

/**
 * 网关配置类
 */
@Configuration
public class GatewayConfig {

    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;
    private final ServerProperties serverProperties;
    private final ApplicationContext applicationContext;
    private final ResourceProperties resourceProperties;

    public GatewayConfig(List<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer, ServerProperties serverProperties, ApplicationContext applicationContext, ResourceProperties resourceProperties) {
        this.viewResolvers = viewResolvers;
        this.serverCodecConfigurer = serverCodecConfigurer;
        this.serverProperties = serverProperties;
        this.applicationContext = applicationContext;
        this.resourceProperties = resourceProperties;
    }

//    @Value("${spring.cloud.nacos.config.namespace}")
//    private String namespace;

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    /**
     * 全局异常处理
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE + 1)
    public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
        ExceptionHandler exceptionHandler = new ExceptionHandler(
                errorAttributes,
                this.resourceProperties,
                this.serverProperties.getError(),
                this.applicationContext);
        exceptionHandler.setViewResolvers(this.viewResolvers);
        exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
        exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
        return exceptionHandler;
    }

    @PostConstruct
    public void doInit() {
        // 自定义Block异常
        GatewayCallbackManager.setBlockHandler(new GatewayLimitBlockException());
        Properties properties = new Properties();
        // remoteAddress 代表 Nacos 服务端的地址IP
        properties.setProperty(PropertyKeyConst.SERVER_ADDR, Constants.REMOTEADDRESS);
//        properties.setProperty(PropertyKeyConst.NAMESPACE, namespace);
        // groupId 和 dataId 对应 Nacos 中相应配置
        String groupId = Constants.GROUPID;
        // 注册API管理对应的自定义分组
        String dataIdApi = Constants.DATAIDAPI;
        NacosDataSource<Set<ApiDefinition>> gatewayApiDataSource = new NacosDataSource<>(properties, groupId, dataIdApi,
                this::returnApi);
        GatewayApiDefinitionManager.register2Property(gatewayApiDataSource.getProperty());
        // 注册网关流控
        String dataIdRule = Constants.DATAIDRULE;
        ReadableDataSource<String, Set<GatewayFlowRule>> flowRuleDataSource = new NacosDataSource<>(properties, groupId, dataIdRule,
                source -> JSON.parseObject(source, new TypeReference<>() {
                }));
        GatewayRuleManager.register2Property(flowRuleDataSource.getProperty());
    }

    public Set<ApiDefinition> returnApi(String json) {
        JSONArray jsonArray = JSON.parseArray(json);
        Set<ApiDefinition> apis = new HashSet<>(16);
        if (!jsonArray.isEmpty()) {
            for (int i = 0; i < jsonArray.size(); i++) {
                ApiDefinition apiDefinition = new ApiDefinition();
                apiDefinition.setApiName(jsonArray.getJSONObject(i).getString("apiName"));
                String predicateItems = jsonArray.getJSONObject(i).getString("predicateItems");
                List<ApiPathPredicateItem> apiPathPredicateItems = JSON.parseArray(predicateItems, ApiPathPredicateItem.class);
                apiDefinition.setPredicateItems(new HashSet<>(apiPathPredicateItems));
                apis.add(apiDefinition);
            }
        }
        return apis;
    }
}
